package com.jijunpeng.javafx.cipher.controller.api;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.jijunpeng.javafx.cipher.CipherToolApplication;
import com.jijunpeng.javafx.cipher.persistence.PersistenceJson;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * @author Ji Junpeng
 * @date 2019-12-13 00:11
 */
@Slf4j
public abstract class BaseController implements Initializable {

    private final JSONObject config = new JSONObject();

    public JSONObject persistWidgets() throws IllegalAccessException {
        log.info("start_save_controller_field_value. controller:{}", this.getClass().getName());
        config.clear();
        PersistenceJson[] classPersistenceJsons = this.getClass().getDeclaredAnnotationsByType(PersistenceJson.class);
        for (Field field : this.getClass().getDeclaredFields()) {
            PersistenceJson[] fieldPersistenceJsons = field.getDeclaredAnnotationsByType(PersistenceJson.class);
            if (ArrayUtil.isEmpty(fieldPersistenceJsons)) {
                continue;
            }
            persistWidget(field, classPersistenceJsons, fieldPersistenceJsons);
        }
        log.info("success_save_controller_field_value. controller:{}", this.getClass().getName());
        return config;
    }

    private void persistWidget(Field field, PersistenceJson[] classPersistenceJsons, PersistenceJson[] fieldPersistenceJsons) throws IllegalAccessException {
        if (ArrayUtil.isEmpty(classPersistenceJsons)) {
            log.error("persistence_error. {} 需要添加注解 PersistenceJson", this.getClass().getName());
            return;
        }
        field.setAccessible(true);
        PersistenceJson classPersistenceJson = classPersistenceJsons[0];
        String classJsonPath = classPersistenceJson.path();
        for (PersistenceJson fieldPersistenceJson : fieldPersistenceJsons) {
            String fieldJsonPath = fieldPersistenceJson.path() + "." + fieldPersistenceJson.field();
            if (!fieldJsonPath.startsWith("$.")) {
                fieldJsonPath = classJsonPath + "." + fieldJsonPath;
            }
            Object widget = field.get(this);
            String getterMethodName = getGetterName(fieldPersistenceJson);
            if (StringUtils.isNotBlank(getterMethodName)) {
                Object value = ReflectUtil.invoke(widget, getterMethodName);
                if (value instanceof Enum) {
                    JSONPath.set(config, fieldJsonPath + "_$type", ((Enum) value).getDeclaringClass().getName());
                }
                JSONPath.set(config, fieldJsonPath, value);
            } else {
                throw new IllegalArgumentException("field的getterMethod 不能为空");
            }
        }
    }

    /**
     * @return 获取当前controller的根pane
     */
    protected abstract Pane getRootPane();

    @SneakyThrows
    @Override
    public final void initialize(URL location, ResourceBundle resources) {
        CipherToolApplication.addController(this);

        initViewData();
        initPersistedValue();
    }

    /**
     * 初始化控件数据
     */
    protected void initViewData() {
    }

    /**
     * 初始化控件的上次状态
     */
    private void initPersistedValue() throws IllegalAccessException {
        JSONObject jsonObject = CipherToolApplication.readConfig(this);
        if (jsonObject==null) {
            return;
        }
        config.putAll(jsonObject.getInnerMap());
        initPersistedValue(config);
    }

    protected void initPersistedValue(JSONObject persistenceJson) throws IllegalAccessException {
        PersistenceJson classPersistenceJson = getClassPersistenceJson();
        if (classPersistenceJson == null) {
            return;
        }
        Map<PersistenceJson, Object> allFieldPersistenceJson = getAllFieldPersistenceJson();
        for (PersistenceJson fieldPersistenceJson : allFieldPersistenceJson.keySet()) {
            String fieldJsonPath = obtainJsonPath(classPersistenceJson, fieldPersistenceJson);
            if (!JSONPath.contains(persistenceJson, fieldJsonPath)) {
                continue;
            }
            Object fieldValue = JSONPath.eval(persistenceJson, fieldJsonPath);
            Object widget = allFieldPersistenceJson.get(fieldPersistenceJson);
            String setterName = getSetterName(fieldPersistenceJson);
            if (JSONPath.contains(persistenceJson, fieldJsonPath+"_$type")) {
                Object enumType = JSONPath.eval(persistenceJson, fieldJsonPath + "_$type");
                try {
                    Class<?> aClass = this.getClass().getClassLoader().loadClass(String.valueOf(enumType));
                    for (Object enumConstant : aClass.getEnumConstants()) {
                        if (((Enum) enumConstant).name().equals(fieldValue)) {
                            ReflectUtil.invoke(widget, setterName, enumConstant);
                            break;
                        }
                    }
                } catch (ClassNotFoundException e) {
                    log.error("init_widget_field_error_no_enum_class. expect enum:{}", enumType);
                }
            }else {
                ReflectUtil.invoke(widget, setterName, fieldValue);
            }
        }
    }

    private PersistenceJson getClassPersistenceJson() {
        PersistenceJson[] annotations = this.getClass().getDeclaredAnnotationsByType(PersistenceJson.class);
        if (ArrayUtil.isEmpty(annotations)) {
            return null;
        }
        return annotations[0];
    }

    private Map<PersistenceJson, Object> getAllFieldPersistenceJson() throws IllegalAccessException {
        Field[] fields = this.getClass().getDeclaredFields();
        HashMap<PersistenceJson, Object> result = new HashMap<>(fields.length * 2);
        for (Field field : fields) {
            PersistenceJson[] fieldAnnotations = field.getAnnotationsByType(PersistenceJson.class);
            if (ArrayUtil.isEmpty(fieldAnnotations)) {
                continue;
            }
            field.setAccessible(true);
            Object widget = field.get(this);
            for (PersistenceJson fieldAnnotation : fieldAnnotations) {
                result.put(fieldAnnotation, widget);
            }
        }
        return result;
    }

    private String obtainJsonPath(PersistenceJson classAnnotation, PersistenceJson fieldAnnotation) {
        if (fieldAnnotation.path().startsWith("$.")) {
            return fieldAnnotation.path() + "." + fieldAnnotation.field();
        } else {
            return classAnnotation.path() + "." + fieldAnnotation.path() + "." + fieldAnnotation.field();
        }
    }

    private String getGetterName(PersistenceJson fieldAnnotation) {
        String fieldName = fieldAnnotation.field();
        if (StringUtils.isNotEmpty(fieldAnnotation.getter())) {
            return fieldAnnotation.getter();
        }
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    private String getSetterName(PersistenceJson fieldAnnotation) {
        String fieldName = fieldAnnotation.field();
        if (StringUtils.isNotEmpty(fieldAnnotation.setter())) {
            return fieldAnnotation.getter();
        }
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }
}
